﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using Zorbo;//some handy extensions and things
using Zorbo.Packets;
using Zorbo.Packets.Ares;
using Zorbo.Interface;

using cb0tProtocol.Packets;

namespace cb0tProtocol
{
    public class cb0tProtocol : IPlugin
    {
        string mydir = "";
        IServer server = null;

        IFormatter formatter = null;


        public object GUI {
            get { return null; }
        }

        public string Directory {
            get { return mydir; }
            set { mydir = value; }
        }

        public void OnPluginLoaded(IServer server) {
            this.server = server;
            this.formatter = new AdvancedFormatter();

            foreach (var user in server.Users) {

                user.Extended["CustomFont"] = null;
                user.Extended["SupportEmote"] = false;
                user.Extended["PublicVoice"] = false;
                user.Extended["PrivateVoice"] = false;
                user.Extended["VoiceIgnore"] = new List<String>();
                user.Extended["CustomEmote"] = new List<ClientEmoteItem>();
            }

            foreach (var user in server.Users)
                OnSendLogin(user);

            this.server.SendAnnounce("cb0tProtocol plugin has been loaded!!");
        }

        public void OnPluginKilled() {

            // Send packets to inform cb0t we've stopped supporting the advanced features
            var voice_support = new Advanced(new ServerVoiceSupport() {
                Enabled = false,
                HighQuality = false
            });

            foreach (var user in server.Users) {

                if (user.LoggedIn) {

                    user.Socket.SendAsync(voice_support);

                    if ((bool)user.Extended["PublicVoice"] ||
                        (bool)user.Extended["PrivateVoice"]) {

                        server.SendPacket(new Advanced(new ServerVoiceSupportUser() {
                            Username = user.Name,
                            Public = false,
                            Private = false,
                        }));
                    }

                    if ((bool)user.Extended["SupportEmote"]) {

                        server.SendPacket(new Advanced(new ServerEmoteSupport(0)));

                        foreach (var emote in (List<ClientEmoteItem>)user.Extended["CustomEmote"])
                            server.SendPacket(new Advanced(new ServerEmoteDelete() {
                                Username = user.Name,
                                Shortcut = emote.Shortcut,
                            }));
                    }
                }

                user.Extended.Remove("CustomFont");
                user.Extended.Remove("SupportEmote");
                user.Extended.Remove("PublicVoice");
                user.Extended.Remove("PrivateVoice");
                user.Extended.Remove("VoiceIgnore");
                user.Extended.Remove("CustomEmote");
            }

            this.server.SendAnnounce("cb0tProtocol plugin has been unloaded!!");
        }

        public void OnCaptcha(IClient client, CaptchaEvent @event) {
        }

        public void OnSendLogin(IClient client) {

            client.Socket.SendAsync(new Advanced(new ServerVoiceSupport() {
                Enabled = true,
                HighQuality = true,
            }));

            client.Socket.SendAsync(new Advanced(new ServerEmoteSupport(16)));

            for (int i = 0; i < server.Users.Count; i++) {
                IClient target = server.Users[i];

                if (target.Connected &&
                    target.LoggedIn &&
                    target.Vroom == client.Vroom) {

                    var font = (ServerFont)target.Extended["CustomFont"];
                    if (font != null) client.Socket.SendAsync(font);

                    bool pubvoice = (bool)target.Extended["PublicVoice"];
                    bool privoice = (bool)target.Extended["PrivateVoice"];

                    if (pubvoice || privoice) {
                        client.Socket.SendAsync(new Advanced(new ServerVoiceSupportUser() {
                            Username = target.Name,
                            Public = pubvoice,
                            Private = privoice,
                        }));
                    }
                }
            }
        }

        public bool OnJoinCheck(IClient client) {

            client.Extended["CustomFont"] = null;
            client.Extended["SupportEmote"] = false;
            client.Extended["PublicVoice"] = false;
            client.Extended["PrivateVoice"] = false;
            client.Extended["VoiceIgnore"] = new List<String>();
            client.Extended["CustomEmote"] = new List<ClientEmoteItem>();

            return true;
        }

        public void OnJoinRejected(IClient client, RejectReason reason) {
        }

        public void OnJoin(IClient client) {
            //Support features sent in OnSendLogin
        }

        public void OnPart(IClient client, Object state) {
        }

        public bool OnVroomJoinCheck(IClient client, UInt16 vroom) {
            return true;
        }

        public void OnVroomJoin(IClient client) {
        }

        public void OnVroomPart(IClient client) {

        }

        public bool OnRegister(IClient client, IPassword password) {
            return true;
        }

        public bool OnFileReceived(IClient client, ISharedFile file) {
            return true;
        }

        public bool OnBeforePacket(IClient client, IPacket packet) {

            if ((AresId)packet.Id == AresId.MSG_CHAT_CLIENT_CUSTOM_DATA) {

                var custom = (ClientCustom)packet;

                if (custom.Username == "Zorbo" && custom.CustomId.StartsWith("cb0t_scribble")) {

                    custom.Username = client.Name;
                    server.SendPacket((s) => s != client && s.Vroom == client.Vroom, custom);
                }
            }

            return true;
        }

        public void OnAfterPacket(IClient client, IPacket packet) {

            
            if ((AdvancedId)packet.Id == AdvancedId.MSG_CHAT_ADVANCED_FEATURES_PROTOCOL) {

                Unknown unknown = (Unknown)packet;

                ushort length = BitConverter.ToUInt16(unknown.Payload, 0);
                IPacket advanced = formatter.Unformat(unknown.Payload[2], unknown.Payload, 3, length);


                switch ((AdvancedId)advanced.Id) {
                    case AdvancedId.MSG_CHAT_CLIENT_CUSTOM_ADD_TAGS:
                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_CUSTOM_REM_TAGS:
                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_CUSTOM_FONT:
                        ClientFont font = (ClientFont)advanced;

                        ServerFont userfont = (ServerFont)client.Extended["CustomFont"];

                        if (userfont == null) {
                            userfont = new ServerFont();
                            client.Extended["CustomFont"] = userfont;
                        }

                        userfont.Username = client.Name;
                        userfont.Size = font.Size;
                        userfont.Name = font.Name;
                        userfont.NameColor = font.NameColor;
                        userfont.TextColor = font.TextColor;

                        Advanced send = new Advanced(userfont);

                        server.SendPacket((s) => s.Vroom == client.Vroom, send);

                        if (String.IsNullOrWhiteSpace(font.Name))
                            client.Extended["CustomFont"] = null;

                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_VC_SUPPORTED:
                        ClientVoiceSupport vcs = (ClientVoiceSupport)advanced;

                        client.Extended["PublicVoice"] = vcs.Public;
                        client.Extended["PrivateVoice"] = vcs.Private;

                        server.SendPacket((s) =>
                            s.Vroom == client.Vroom,
                            new Advanced(new ServerVoiceSupportUser() {
                                Username = client.Name,
                                Public = vcs.Public,
                                Private = vcs.Private,
                            }));
                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_VC_FIRST:
                        ClientVoiceFirst vcf = (ClientVoiceFirst)advanced;

                        server.SendPacket((s) =>
                            s.Vroom == client.Vroom &&
                            (bool)s.Extended["PublicVoice"] &&
                            !((List<String>)s.Extended["VoiceIgnore"]).Contains(client.Name),

                            new Advanced(new ServerVoiceFirst() {
                                Username = client.Name,
                                Chunk = vcf.Chunk
                            }));

                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_VC_FIRST_TO: {

                            ClientVoiceFirstTo vcf2 = (ClientVoiceFirstTo)advanced;

                            IClient target = server.FindUser((s) => 
                                s.Name == vcf2.Username &&
                                s.Vroom == client.Vroom);

                            if (target == null) return;

                            if (!(bool)target.Extended["PrivateVoice"])
                                client.Socket.SendAsync(new Advanced(new ServerVoiceNoPrivate() {
                                    Username = target.Name
                                }));

                            else if (((List<String>)target.Extended["VoiceIgnore"]).Contains(client.Name))
                                client.Socket.SendAsync(new Advanced(new ServerVoiceIgnore() {
                                    Username = target.Name
                                }));

                            else target.Socket.SendAsync(new Advanced(new ServerVoiceFirstFrom() {
                                Username = client.Name,
                                Chunk = vcf2.Chunk
                            }));
                        }
                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_VC_CHUNK:
                        ClientVoiceChunk vcc = (ClientVoiceChunk)advanced;

                        server.SendPacket((s) =>
                            s.Vroom == client.Vroom &&
                            (bool)s.Extended["PublicVoice"] &&
                            !((List<String>)s.Extended["VoiceIgnore"]).Contains(client.Name),

                            new Advanced(new ServerVoiceChunk() {
                                Username = client.Name,
                                Chunk = vcc.Chunk
                            }));

                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_VC_CHUNK_TO: {

                            ClientVoiceChunkTo vcc2 = (ClientVoiceChunkTo)advanced;

                            IClient target = server.FindUser((s) => 
                                s.Name == vcc2.Username &&
                                s.Vroom == client.Vroom);

                            if (target == null) return;

                            if (!(bool)target.Extended["PrivateVoice"])
                                client.Socket.SendAsync(new Advanced(new ServerVoiceNoPrivate() {
                                    Username = target.Name
                                }));

                            else if (((List<String>)target.Extended["VoiceIgnore"]).Contains(client.Name))
                                client.Socket.SendAsync(new Advanced(new ServerVoiceIgnore() {
                                    Username = target.Name
                                }));

                            else target.Socket.SendAsync(new Advanced(new ServerVoiceFirstFrom() {
                                Username = client.Name,
                                Chunk = vcc2.Chunk
                            }));
                        }
                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_VC_IGNORE:
                        ClientVoiceIgnore vci = (ClientVoiceIgnore)advanced;
                        List<String> ignores = (List<String>)client.Extended["VoiceIgnore"];

                        if (ignores.Contains(vci.Username)) {

                            ignores.RemoveAll((s) => s == vci.Username);
                            server.SendAnnounce(client, String.Format("You are now allowing voice chat from {0}", vci.Username));
                        }
                        else {
                            ignores.Add(vci.Username);
                            server.SendAnnounce(client, String.Format("You are now ignoring voice chat from {0}", vci.Username));
                        }
                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_SUPPORTS_CUSTOM_EMOTES:
                        client.Extended["SupportEmote"] = true;

                        foreach (var user in server.Users) {
                            if (user.Vroom == client.Vroom) {

                                var emotes = (List<ClientEmoteItem>)client.Extended["CustomEmote"];

                                if (emotes.Count > 0) {
                                    foreach (var emote in emotes)
                                        client.Socket.SendAsync(new Advanced(new ServerEmoteItem() {
                                            Username = user.Name,
                                            Shortcut = emote.Shortcut,
                                            Size = emote.Size,
                                            Image = emote.Image,
                                        }));
                                }
                            }
                        }
                        break;
                    case AdvancedId.MSG_CHAT_SERVER_CUSTOM_EMOTES_ITEM: {
                            client.Extended["SupportEmote"] = true;
                            ClientEmoteItem item = (ClientEmoteItem)advanced;

                            ((List<ClientEmoteItem>)client.Extended["CustomEmote"]).Add(item);

                            if (client.Cloaked) {
                                server.SendPacket((s) =>
                                    s.Admin >= client.Admin &&
                                    s.Vroom == client.Vroom &&
                              (bool)s.Extended["SupportEmote"],
                                    new Advanced(new ServerEmoteItem() {
                                        Username = client.Name,
                                        Shortcut = item.Shortcut,
                                        Size = item.Size,
                                        Image = item.Image,
                                    }));
                            }
                            else {
                                server.SendPacket((s) =>
                                    s.Vroom == client.Vroom &&
                              (bool)s.Extended["SupportEmote"],
                                    new Advanced(new ServerEmoteItem() {
                                        Username = client.Name,
                                        Shortcut = item.Shortcut,
                                        Size = item.Size,
                                        Image = item.Image,
                                    }));
                            }
                        }
                        break;
                    case AdvancedId.MSG_CHAT_CLIENT_CUSTOM_EMOTE_DELETE: {
                            ClientEmoteDelete item = (ClientEmoteDelete)advanced;

                            var emotes = ((List<ClientEmoteItem>)client.Extended["CustomEmote"]);
                            int index = emotes.FindIndex(s => s.Shortcut == item.Shortcut);

                            if (index > -1) {
                                emotes.RemoveAt(index);

                                server.SendPacket((s) =>
                                    s.Vroom == client.Vroom &&
                              (bool)s.Extended["SupportEmote"],
                                    new Advanced(new ServerEmoteDelete() {
                                        Username = client.Name,
                                        Shortcut = item.Shortcut,
                                    }));
                            }
                        }
                        break;
                }
            }
        }

        public bool OnFlood(IClient client, IPacket packet) {
            return true;
        }

        public void OnError(IErrorInfo error) {
            server.SendAnnounce(error.Name + " has caused: " + error.Exception.Message);
        }
    }
}
